// This file is a part of ChaosDB.
// Copyright (C) 2012 ChaoJianPeng
// email:chaojianpeng@gmail.com
//
// ChaosDB is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// ChaosDB is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

/*
 * cdb.cpp
 *
 *  Created on: 2012-3-6
 *      Author: chao
 */

#include <uthash.h>
#include "cdb.h"
#include "conf.h"
#include "logger.h"
#include "cmalloc.h"
#include "respond.h"
#include "bdb_api.h"
#include "bdb.h"
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>

HashMap* hm;
LruList* l;

char* OPEN_DICT_FAIL = "Couldn't open the directory";

void dbInit() {
	// 创建hashmap
	hm = hmCreat();
	// 创建lru表
	l = lruCreat();
	// 初始化后台io线程
	bioInit();
}

int dbAdd(char* key, void* value, int keyLen, int valueLen) {
	// 尝试插入
	Entry* entry = hmAdd(hm, key, value, keyLen);
	// key已经存在
	if (entry == NULL) {
		return DB_CMD_ERR;
	}
	// 加入lru
	lruAdd(l, entry);
	// 检查lru
	lruCheckLru(l);
	// 写入文件
	fileWrite(key, value, keyLen, valueLen);
	return DB_CMD_OK;
}

void* dbGet(char* key, int keyLen) {
	// 尝试查找
	Entry* entry = hmGet(hm, key);
	// key不存在
	if (entry == NULL) {
		return NULL;
	}
	// key存在,value不存在
	if (entry->value == NULL) {
		void* v = fileRead(key, keyLen);
		if (v == NULL) {
			return NULL;
		}
		entry->value = v;
		// 重新加入lru
		lruAdd(l, entry);
		// 检查lru
		lruCheckLru(l);
	}
	// 正常返回,刷新lru
	else {
		lruToHead(l, entry);
	}
	return entry->value;
}

int dbSet(char* key, void* value, int keyLen, int valueLen) {
	Entry* entry = hmSet(hm, key, value, keyLen);
	// 重新加入lru
	lruToHead(l, entry);
	// 检查lru
	lruCheckLru(l);
	// 写入文件
	fileWrite(key, value, keyLen, valueLen);
	return DB_CMD_OK;
}

int dbReplace(char* key, void* value, int keyLen, int valueLen) {
	Entry* entry = hmReplace(hm, key, value, keyLen);
	// key不存在
	if (entry == NULL) {
		return DB_CMD_ERR;
	}
	// 重新加入lru
	lruToHead(l, entry);
	// 写入文件
	fileWrite(key, value, keyLen, valueLen);
	return DB_CMD_OK;
}

int dbRemove(char* key, int keyLen) {
	Entry* entry = hmDel(hm, key);
	// key不存在
	if (entry == NULL) {
		return DB_CMD_ERR;
	}
	// 移除lru
	lruRemove(l, entry);
	// 移除文件
	fileDel(key, keyLen);
	// 清空内存
	entryDel(entry);
	cfree(entry);
	return DB_CMD_OK;
}

/**
 * 结果需要用cfree释放entry->value
 */
//void* dbGetAndRemove(char* key, int keyLen) {
//	void* res;
//	Entry* entry = hmDel(hm, key);
//	// key不存在
//	if (entry == NULL) {
//		return NULL;
//	}
//	// key存在,value不存在
//	if (entry->value == NULL) {
//		void* v = fileRead(key, keyLen);
//		if (v == NULL) {
//			return NULL;
//		}
//		entry->value = v;
//	}
//
//	res = entry->value;
//
//	// 移除lru
//	lruRemove(l, entry);
//	// 移除文件
//	fileDel(key, keyLen);
//	// 清空内存
//	cfree(entry->key);
//	cfree(entry);
//	return res;
//}
void dbRebuild() {
	if (STORAGE_TYPE == DEFAULT_BIO) {
		DIR *dp;
		struct dirent *ep;
		dp = opendir("data/");

		if (dp != NULL) {
			while ((ep = readdir(dp))) {
				if (strcmp(".", ep->d_name) == 0
						|| strcmp("..", ep->d_name) == 0)
					continue;
				int len = strlen(ep->d_name);
				char* key = (char*) cmalloc(len + 1);
				memcpy(key, ep->d_name, len + 1);
				hmAdd(hm, key, NULL, len);
			}
			closedir(dp);
		}

		else {
			systemLogger(OPEN_DICT_FAIL);
		}
	} else if (STORAGE_TYPE == BERKELET_DB_BIO) {
		DB* dbp = getDb();
		DBC* cursorp;
		dbp->cursor(dbp, NULL, &cursorp, 0);

		DBT name, data;
		initDBT(&name);
		initDBT(&data);
		// 遍历
		while (cursorp->get(cursorp, &name, &data, DB_NEXT) == 0) {
			char* key = (char*) cmalloc(name.size);
			memcpy(key, name.data, name.size);
			hmAdd(hm, key, NULL, name.size);
		}
		free(cursorp);
	}
}

void dbBak() {
	bdbBakInit();
	Entry* current_entry;
	Entry* tmp;
	void* v;
	int keyLen;
	HASH_ITER(hh, hm->ht, current_entry, tmp) {
		keyLen = strlen(current_entry->key);
		if(current_entry->value == NULL){
			v = fileRead(current_entry->key, keyLen);
			bdbBak(current_entry->key, keyLen, v, getSize(v));
			cfree(v);
		}
		else {
			bdbBak(current_entry->key, keyLen, current_entry->value, getSize(current_entry->value));
		}
	}
	bdbBakEnd();
}

int dbInfo() {
	return hmSize(hm);
}
